package com.kkarma.util.service;

import com.kkarma.util.redis.RedisCache;
import com.kkarma.vo.LoginUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author: karma
 * @Date: 2022/3/30 0030 - 03 - 30 - 9:05
 * @Description: com.kkarma.util.service
 * @version: 1.0
 */
@Component
public class TokenService {

    private final RedisCache redisCache;

    @Autowired
    public TokenService(RedisCache redisCache) {
        this.redisCache = redisCache;
    }

    /**
    * method description: 创建token
    * @param: LoginUser 登录接口的DTO对象
    * @return: token 身份令牌
    * @author: kkarma
    * @date: 2022-03-30 11:13:20
    */
    public String createToken(LoginUser loginUser) {
        // 给每一个登录用户生成一个唯一的token key字符串
        String tokenKey = UUID.randomUUID().toString().replace("-", "");
        // 将token key的字符串保存到LoginUser实体类的token属性中
        loginUser.setToken(tokenKey);
        // 将当前登录用户的实体类对象缓存到redis数据库中， 同时设置初始的默认过期时间为30分钟
        // 用户登录之后的每次请求都刷新这个redis缓存中保存的loginUser实体类对象的expireTime属性，
        // 保证只要用户访问活跃， token自动刷新不过期
        refreshToken(loginUser);
        Map<String, Object> claims = new HashMap<>();
        // 缓存用户的登录信息
        claims.put("login_user_uni_key", tokenKey);
        // 生成token
        return createToken(claims);
    }

    /**
     * method description: 自动重置登录用户的过期时间并缓存登录用户信息到redis
     * @param: LoginUser 登录接口的DTO对象
     * @return: token 身份令牌
     * @author: kkarma
     * @date: 2022-03-30 11:13:20
     */
    private void refreshToken(LoginUser loginUser) {
        // 设置该用户的登录时间
        loginUser.setLoginTime(System.currentTimeMillis());
        // 同时设置当前用户的过期时间 = 该用户的登录时间 + 指定的过期时间间隔(默认为半小时)
        loginUser.setExpireTime(loginUser.getLoginTime() + 30 * 60 * 1000);
        // 使用之前生成的tokenkey, 把当前用户的信息缓存到redis中
        redisCache.setCacheObject(getTokenKey(loginUser.getToken()), loginUser, 30, TimeUnit.MINUTES);
    }

    /**
     * method description: 生成缓存登录用户的唯一token key信息的方法
     * @param:
     * @return: token 身份令牌
     * @author: kkarma
     * @date: 2022-03-30 11:13:20
     */
    public String getTokenKey(String uniTokenKey) {
        return "login_token:" + uniTokenKey;
    }


    /**
     * method description: 创建token的具体操作
     * @param: claims 身份凭证对象信息
     * @return: token 身份令牌
     * @author: kkarma
     * @date: 2022-03-30 11:13:20
     */
    public String createToken(Map<String, Object> claims) {
        return Jwts.builder()
                // 设置payload
                .setClaims(claims)
                // 设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
                // 生成签名
                .signWith(SignatureAlgorithm.HS512, "mouseLoverice2022")
                // token合成
                .compact();
    }

    /**
     * method description: 解析Token
     * @param: token 身份令牌身份凭证对象信息
     * @return: Claims 身份凭证对象信息
     * @author: kkarma
     * @date: 2022-03-30 11:13:20
     */
    public Claims parseToken(String token)
    {
        return Jwts.parser()
                .setSigningKey("mouseLoverice2022")
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * method description: 验证登录用户的过期时间并自动重置过期时间
     * @param: loginUser 登录用户DTO对象
     * @return:
     * @author: kkarma
     * @date: 2022-03-30 11:13:20
     */
    public void verifyToken(LoginUser loginUser) {
        Long expireTime = loginUser.getExpireTime();
        Long currentTime = System.currentTimeMillis();
        // 如果登录用户的过期时间小于30分钟，重置用户过期时间为30分钟
        if(expireTime - currentTime < 30 * 60 * 1000L) {
            refreshToken(loginUser);
        }
    }
}
